package com.usb2uartagent;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbManager;
import android.os.Bundle;

import com.csjbot.csjbase.log.Csjlogger;
import com.csjbot.snowbot_rogue.utils.Constant;
import com.expression.uartagent.ExpressionUartAgent;
import com.expression.uartagent.core.UsbUartExpressionHelper;
import com.expression.uartagent.entity.UartPacketExpression;
import com.iflytek.aiui.uartkit.core.UsbUARTConnectorHelper;
import com.iflytek.aiui.uartkit.entity.DataPacket;
import com.upcontrol.uartagent.BodyUartAgent;
import com.upcontrol.uartagent.core.Usb2UartUpControlHelper;
import com.upcontrol.uartagent.utils.PacketBuilder;

import java.util.Arrays;

import tw.com.prolific.pl2303multilib.PL2303MultiLib;

/**
 * Copyright (c) 2017, SuZhou CsjBot. All Rights Reserved.
 * www.csjbot.com
 * <p>
 * Created by 浦耀宗 at 2017/02/09 0009-14:06.
 * Email: puyz@csjbot.com
 */

public class Usb2UartagentManager {
    private volatile PL2303MultiLib mPL2303MultiLib;
    public static final String TAG = "Usb2UartagentManager";
    private static final String ACTION_USB_PERMISSION = "com.csjbot.snowbot.USB_PERMISSION";

    //定义各个模块对应的USB序号
    public static final int AIUI_TYPE = 0x0;
    public static final int EXPRESSION_TYPE = 0x1;
    public static final int UP_CONTROL_TYPE = 0x2;
    public static final int MAX_USB2UARTAGENT_NUM = 3;

    private int aiuiIndex = 0;
    private int expressionIndex = 1;
    private int upControlIndex = 2;


    private ReadRunnable[] mReadRunnables;
    private Context mContext;

    class UARTSettingInfo {
        public int iPortIndex = 0;
        public PL2303MultiLib.BaudRate mBaudrate = PL2303MultiLib.BaudRate.B115200;
        public PL2303MultiLib.DataBits mDataBits = PL2303MultiLib.DataBits.D8;
        public PL2303MultiLib.Parity mParity = PL2303MultiLib.Parity.NONE;
        public PL2303MultiLib.StopBits mStopBits = PL2303MultiLib.StopBits.S1;
        public PL2303MultiLib.FlowControl mFlowControl = PL2303MultiLib.FlowControl.OFF;
    }

    private UARTSettingInfo[] mUARTSettingInfo;

    private static Usb2UartagentManager mUsb2UartagentManager = new Usb2UartagentManager();

    public static Usb2UartagentManager getInstance() {
        return mUsb2UartagentManager;
    }

    private Usb2UartagentManager() {
        mUARTSettingInfo = new UARTSettingInfo[MAX_USB2UARTAGENT_NUM];
        mReadRunnables = new ReadRunnable[MAX_USB2UARTAGENT_NUM];
        for (int i = 0; i < MAX_USB2UARTAGENT_NUM; i++) {
            UARTSettingInfo uartSettingInfo = new UARTSettingInfo();
            uartSettingInfo.iPortIndex = i;
            mUARTSettingInfo[i] = uartSettingInfo;
            ReadRunnable runnable = new ReadRunnable(i);
            mReadRunnables[i] = runnable;
        }

    }


    /**
     * 初始化管理器，此方法必须在使用管理器之前至少调用一次
     *
     * @param context
     */
    public void initUsb2UartagentManager(Context context) {
        if (context == null) {
            Csjlogger.error("initUsb2UartagentManager context is null !");
            return;
        }
        if (mPL2303MultiLib == null) {
            synchronized (this) {
                if (mPL2303MultiLib == null) {
                    mPL2303MultiLib = new PL2303MultiLib((UsbManager) context.getSystemService(Context.USB_SERVICE),
                            context, ACTION_USB_PERMISSION);
                    int deviceNum = mPL2303MultiLib.PL2303Enumerate();
                    Csjlogger.debug("detect usb device num is " + deviceNum);
                    if (deviceNum > 0) {
                        IntentFilter filter = new IntentFilter();
                        filter.addAction(mPL2303MultiLib.PLUART_MESSAGE);
                        filter.addAction(ACTION_USB_PERMISSION);
                        mContext = context;
//                        mContext.registerReceiver(mPLMultiLibReceiver, filter);
                    }
                }
            }
        } else {
            Csjlogger.debug("Usb2UartagentManager is inited over!");
        }
    }

    public int init(int index) {
        if (!isValidIndex(index)) {
            Csjlogger.error("index is error!");
            return -1;
        }

        if (mPL2303MultiLib == null) {
            Csjlogger.error("mPL2303MultiLib is null!");
            return -1;
        }

        if (mPL2303MultiLib.PL2303Enumerate() < (index + 1)) {
            Csjlogger.error(index + " is not able Device index!");
            return -1;
        }

        if (!mPL2303MultiLib.PL2303IsDeviceConnectedByIndex(index)) {
            Csjlogger.error(index + " is not connected Device!");
            return -1;
        }

        boolean res;
        mPL2303MultiLib.PL2303CloseDeviceByIndex(index);
        UARTSettingInfo info = mUARTSettingInfo[index];
        res = mPL2303MultiLib.PL2303OpenDevByUARTSetting(index, info.mBaudrate, info.mDataBits, info.mStopBits,
                info.mParity, info.mFlowControl);
        if (!res) {
            Csjlogger.error("open " + index + " usb Device failed !");
            return -1;
        }


        if (mReadRunnables[index].isbStop()) {
            mReadRunnables[index].startRead();
        } else {
            Csjlogger.error(index + "usb device is already working!");
        }

        return 0;
    }

    public int init(int index, int speed) {
        UARTSettingInfo info = mUARTSettingInfo[index];
        info.mBaudrate = getSpeed(speed);
        return init(index);
    }

    public void setDataHandle(int index, DataHandler dataHandle) {
        if (!isValidIndex(index)) {
            Csjlogger.error("index is error!");
            return;
        }
        mReadRunnables[index].setDataHandler(dataHandle);
    }


    private boolean isValidIndex(int index) {
        if (index >= 0 && index < MAX_USB2UARTAGENT_NUM) {
            return true;
        } else {
            return false;
        }
    }

    class ReadRunnable implements Runnable {
        private int deviceIndex;
        private static final long READ_INTERVAL = 60;
        private byte[] readBuf = new byte[4096];
        private boolean bStop = true;
        private DataHandler dataHandler;
        private int detectType = -1;

        @Override
        public void run() {
            while (!bStop) {
                int readLen = 0;
                readLen = mPL2303MultiLib.PL2303Read(deviceIndex, readBuf);
                if (readLen > 0) {
                    Csjlogger.debug(deviceIndex + " usb recv " + readLen + " bytes!");
                    if (dataHandler != null) {
                        dataHandler.handleData(Arrays.copyOf(readBuf, readLen), readLen);
                    }
                }
//                else
//                {
//                   Csjlogger.error(deviceIndex + " usb recv " + readLen + " bytes!");
//                }
                try {
                    Thread.sleep(READ_INTERVAL);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return;
                }
            }
        }

        public void stopRead() {
            bStop = true;
            Thread.currentThread().interrupt();
        }

        public ReadRunnable(int deviceIndex) {
            this.deviceIndex = deviceIndex;
        }

        public void setDataHandler(DataHandler dataHandler) {
            this.dataHandler = dataHandler;
        }

        public boolean isbStop() {
            return bStop;
        }

        public void startRead() {
            bStop = false;
            new Thread(this).start();
        }

        public void setDetectType(int detectType) {
            this.detectType = detectType;
        }

        public int getDetectType() {
            return detectType;
        }
    }

    private int getIndexByType(int type) {
        switch (type) {
            case AIUI_TYPE:
                return aiuiIndex;
            case EXPRESSION_TYPE:
                return expressionIndex;
            case UP_CONTROL_TYPE:
                return upControlIndex;
        }
        return aiuiIndex;
    }

    public int sendData(int type, byte[] data) {
        int index = getIndexByType(type);
        if (!isValidIndex(index)) {
            Csjlogger.error("index is error!");
            return -1;
        }

        if (mPL2303MultiLib == null) {
            Csjlogger.error("mPL2303MultiLib is null!");
            return -1;
        }

        if (mPL2303MultiLib.PL2303Enumerate() < (index + 1)) {
            Csjlogger.error(index + " is not able Device index!");
            return -1;
        }

        if (!mPL2303MultiLib.PL2303IsDeviceConnectedByIndex(index)) {
            Csjlogger.error(index + " is not connected Device!");
            return -1;
        }

        if (mReadRunnables[index].isbStop()) {
            boolean res;
            UARTSettingInfo info = mUARTSettingInfo[index];
            mPL2303MultiLib.PL2303CloseDeviceByIndex(index);
            res = mPL2303MultiLib.PL2303OpenDevByUARTSetting(index, info.mBaudrate, info.mDataBits, info.mStopBits,
                    info.mParity, info.mFlowControl);
            if (!res) {
                Csjlogger.error("open " + index + " usb Device failed !");
                return -1;
            }
            mReadRunnables[index].startRead();
        }

        int sendLen = 0;

//        int res = mPL2303MultiLib.PL2303Write(index, data);
//        if (res < 0) {
//           Csjlogger.error(index + " usb device sendData " + data.length + " bytes " + " failed!");
//            return -1;
//        }
        while (sendLen < data.length) {
            int res = mPL2303MultiLib.PL2303Write(index, Arrays.copyOfRange(data, sendLen, data.length));
            if (res < 0) {
                Csjlogger.error(index + " usb device sendData " + data.length + " bytes " + " failed!");
                return -1;
            } else {
                sendLen += res;
            }
        }
        Csjlogger.debug(index + " usb device sendData " + data.length + " bytes " + " success!");
        return 0;
    }


    /**
     * 如果需要对数据进行拼包、拆包的处理，实现此接口
     */
    public interface DataHandler {
        void handleData(byte[] data, int length);
    }

    private PL2303MultiLib.BaudRate getSpeed(int speed) {
        PL2303MultiLib.BaudRate rate;
        switch (speed) {
            case 75:
                rate = PL2303MultiLib.BaudRate.B75;
                break;
            case 300:
                rate = PL2303MultiLib.BaudRate.B300;
                break;
            case 1200:
                rate = PL2303MultiLib.BaudRate.B1200;
                break;
            case 2400:
                rate = PL2303MultiLib.BaudRate.B2400;
                break;
            case 4800:
                rate = PL2303MultiLib.BaudRate.B4800;
                break;
            case 9600:
                rate = PL2303MultiLib.BaudRate.B9600;
                break;
            case 14400:
                rate = PL2303MultiLib.BaudRate.B14400;
                break;
            case 19200:
                rate = PL2303MultiLib.BaudRate.B19200;
                break;
            case 57600:
                rate = PL2303MultiLib.BaudRate.B57600;
                break;
            case 115200:
                rate = PL2303MultiLib.BaudRate.B115200;
                break;
            case 614400:
                rate = PL2303MultiLib.BaudRate.B614400;
                break;
            case 921600:
                rate = PL2303MultiLib.BaudRate.B921600;
                break;
            case 1228800:
                rate = PL2303MultiLib.BaudRate.B1228800;
                break;
            case 3000000:
                rate = PL2303MultiLib.BaudRate.B3000000;
                break;
            case 6000000:
                rate = PL2303MultiLib.BaudRate.B6000000;
                break;
            default:
                rate = PL2303MultiLib.BaudRate.B9600;
                break;
        }
        return rate;
    }

    private final BroadcastReceiver mPLMultiLibReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(mPL2303MultiLib.PLUART_MESSAGE)) {
                Bundle extras = intent.getExtras();
                if (extras != null) {
                    String str = (String) extras.get(mPL2303MultiLib.PLUART_DETACHED);
                    int index = Integer.valueOf(str);
                    Csjlogger.error(index + " usb device has detached!");
                    if (!mReadRunnables[index].isbStop()) {
                        mReadRunnables[index].stopRead();
                    }
                }
            } else if (intent.getAction().equals(ACTION_USB_PERMISSION)) {
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    Csjlogger.error("GET PERMISSION !");
                }
            }
        }
    };

    /**
     * 释放所有USB转串口资源,停止所有读线程
     */
    public void releaseAll() {
        if (mPL2303MultiLib != null) {
            for (int i = 0; i < MAX_USB2UARTAGENT_NUM; i++) {
                if (!mReadRunnables[i].isbStop()) {
                    mReadRunnables[i].stopRead();
                    mPL2303MultiLib.PL2303CloseDeviceByIndex(i);
                }
            }
            if (mPL2303MultiLib.PL2303Enumerate() > 0) {
                if (mContext != null) {
                    mContext.unregisterReceiver(mPLMultiLibReceiver);
                }
            }
            mPL2303MultiLib.PL2303Release();
            mPL2303MultiLib = null;
        }
        Csjlogger.debug("mPL2303MultiLib release!");
    }

    /**
     * 释放index对应的USB转串口资源
     *
     * @param index
     */
    public void release(int index) {
        if (!isValidIndex(index)) {
            Csjlogger.error("index is error!");
            return;
        }

        if (!mReadRunnables[index].isbStop()) {
            mReadRunnables[index].stopRead();
            mPL2303MultiLib.PL2303CloseDeviceByIndex(index);
        }
    }

    synchronized public int detectUartIndex(int type, DataHandler dataHandler, int speed) {
        switch (type) {
            case AIUI_TYPE:
                Csjlogger.debug("detect UAIUI_TYPE index begin!");
                break;
            case EXPRESSION_TYPE:
                Csjlogger.debug("detect EXPRESSION_TYPE index begin!");
                break;
            case UP_CONTROL_TYPE:
                Csjlogger.debug("detect UP_CONTROL_TYPE index begin!");
                break;
            default:
                Csjlogger.error("unknow Uart Type !");
                return -1;
        }

        for (int i = 0; i < MAX_USB2UARTAGENT_NUM; i++) {
            setDataHandle(i, dataHandler);
            if (!mReadRunnables[i].isbStop()) {
                Csjlogger.error(i + " usb is Running while detect + " + type + " !");
                continue;
            }
            if (init(i, speed) == 0) {
                int count = 0;
                while (count < 10) {
                    switch (type) {
                        case AIUI_TYPE:
                            aiuiIndex = i;
                            sendData(AIUI_TYPE, DataPacket.buildDataPacket(com.iflytek.aiui.uartkit.util.PacketBuilder.obtainHandShakeMsg()).encodeBytes());
                            if (UsbUARTConnectorHelper.bHandleMsgOk) {
                                Csjlogger.debug("detect UAIUI_TYPE index success, index is " + aiuiIndex);
                                mPL2303MultiLib.PL2303CloseDeviceByIndex(i);
                                mReadRunnables[i].stopRead();
                                init(i, speed);
                                return 0;
                            }
                            break;
                        case EXPRESSION_TYPE:
                            expressionIndex = i;
                            ExpressionUartAgent.getExpressionUartAgent().sendMessage(new UartPacketExpression(Constant.Expression.EXPRESSION_TEST));
                            if (UsbUartExpressionHelper.isbHandleMsgOk()) {
                                Csjlogger.debug("detect EXPRESSION_TYPE index success, index is " + expressionIndex);
                                return 0;
                            }
                            break;
                        case UP_CONTROL_TYPE:
                            upControlIndex = i;
                            BodyUartAgent.getBodyUartAgent().sendMessage(PacketBuilder.createRequireUpBodySNPacket());
                            if (Usb2UartUpControlHelper.isbHandleMsgOk()) {
                                Csjlogger.debug("detect UP_CONTROL_TYPE index success, index is " + upControlIndex);
                                return 0;
                            }
                            break;
                    }
                    try {
                        Thread.sleep(1000);
                        count++;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                mPL2303MultiLib.PL2303CloseDeviceByIndex(i);
                mReadRunnables[i].stopRead();
                setDataHandle(i, null);
            }

        }

        switch (type) {
            case AIUI_TYPE:
                aiuiIndex = -1;
                Csjlogger.error("detect UAIUI_TYPE index failed!");
                break;
            case EXPRESSION_TYPE:
                expressionIndex = -1;
                Csjlogger.error("detect EXPRESSION_TYPE index failed!");
                break;
            case UP_CONTROL_TYPE:
                upControlIndex = -1;
                Csjlogger.error("detect UP_CONTROL_TYPE index failed!");
                break;
        }
        return -1;
    }

    public void releaseByType(int type) {
        release(getIndexByType(type));
    }
}
